package com.recharge.service.httpInterFace;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.recharge.common.result.ResultDO;
import com.recharge.common.result.ResultSupport;
import com.recharge.common.util.Constant;
import com.recharge.common.util.LocalDateUtils;
import com.recharge.common.util.MD5Util;
import com.recharge.common.util.http.HttpClientUtil;
import com.recharge.common.util.http.MapTool;
import com.recharge.domain.query.OrderStatusQueryDO;
import com.recharge.domain.vo.GoodsChannelDO;
import com.recharge.domain.vo.ProviderApiDO;
import com.recharge.domain.vo.ProviderCallbackDO;
import com.recharge.service.ProviderApiService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

@Service
public class GuangXianHTTPImpl implements ProviderHTTPService {

    private Logger logger = LoggerFactory.getLogger(this.getClass().getName());

    @Autowired
    ProviderApiService providerApiService;

    @Override
    public ResultDO commitOrder(GoodsChannelDO domain) {
        ProviderCallbackDO callbackDO = new ProviderCallbackDO();
        callbackDO.setSerialNumber(domain.getProviderSerialNumber());
        callbackDO.setProviderId(domain.getProviderId());
        callbackDO.setOrderNo(domain.getOrderNo());
        callbackDO.setStatus(Constant.CALL_BACK_PROCESS);
        ResultDO result = providerApiService.addCallback(callbackDO);
        if (!result.isSuccess()) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 订单%s添加存根信息失败", domain.getProviderSerialNumber())
                    , "添加回调存根信息失败"
                    , null);
        }
        //mrch_no
        String mrch_no = domain.getUserId();
        // 秒级时间戳
        String request_time = LocalDateUtils.getTodayByFormat(LocalDateUtils.DATE_FORMAT_SHORT6);
        //平商户订单号 client_order_no
        String client_order_no = domain.getProviderSerialNumber();
        //产品类型
        String product_type = "1";
        if(Constant.BUSINESS_GOODS_PHONE_FEE == Integer.parseInt(domain.getGoodsType())){
            product_type = "1";
        } else if(Constant.BUSINESS_GOODS_TELEPHONE_FEE == Integer.parseInt(domain.getGoodsType())){
            product_type = "3";
        } else if(Constant.BUSINESS_GOODS_NETWORK_FEE == Integer.parseInt(domain.getGoodsType())){
            product_type = "2";
        }
        //充值号码
        String phone_no = domain.getRechargeNumber();
        //运营商
        String cp = "01";
        switch (domain.getServiceProvider()){
            case 1:
                cp = "01";
                break;
            case 2:
                cp = "02";
                break;
            default:
                cp = "03";
                break;
        }
        String city_code = "";
        //充值面额/流量规格
        Long recharge_amount = domain.getAmount() / 10;
        //支付方式 0: 从预存金扣费
        int recharge_type = 0;
        //充值描述
        String recharge_desc = "";
        //回调地址
        String notify_url = domain.getCallback();
        //供应商密钥 app_secret
        String privateKey = domain.getPrivateKey();
        //签名
        String afterSign = "";
        TreeMap<String, Object> requestParamMap = new TreeMap<>();
        try {
            requestParamMap.put("mrch_no", mrch_no);
            requestParamMap.put("request_time", request_time);
            requestParamMap.put("client_order_no", client_order_no);
            requestParamMap.put("product_type", product_type);
            requestParamMap.put("phone_no", phone_no);
            requestParamMap.put("cp", cp);
            requestParamMap.put("city_code", city_code);
            requestParamMap.put("recharge_amount", recharge_amount);
            requestParamMap.put("recharge_type", recharge_type);
            requestParamMap.put("recharge_desc", recharge_desc);
            requestParamMap.put("notify_url", notify_url);
            String beforeSign = (MapTool.join(requestParamMap, "")).replace("=", "") + privateKey;
            logger.debug("before_sign: " + beforeSign);
            afterSign = MD5Util.md5(beforeSign).toLowerCase();
        } catch (Exception e) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("渠道商ID: %s HTTP请求参数构建失败", domain.getProviderId())
                    , "HTTP请求参数构建失败"
                    , null);
        }
        requestParamMap.put("sign", afterSign);
        String sendResult = HttpClientUtil.doPostJson(domain.getRequestUrl(), JSONObject.toJSONString(requestParamMap));
        switch (sendResult) {
            case "500":
                logger.info(String.format("查询订单 >> 订单 %s -> HTTP请求失败|500", domain.getProviderSerialNumber()));
                return returnFail(false
                        , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                        , String.format("订单提交 >> 供货流水 %s -> HTTP请求失败|500", domain.getProviderSerialNumber())
                        , "HTTP请求失败"
                        , sendResult);
            case "504":
                logger.info(String.format("查询订单 >> 订单 %s -> HTTP请求超时|504", domain.getProviderSerialNumber()));
                return returnFail(false
                        , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                        , String.format("订单提交 >> 供货流水 %s -> HTTP请求超时|5046", domain.getProviderSerialNumber())
                        , "HTTP请求超时"
                        , sendResult);
            case "":
                logger.info(String.format("查询订单 >> 订单 %s -> 请求没有返回信息", domain.getProviderSerialNumber()));
                return returnFail(false
                        , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                        , String.format("订单提交 >> 供货流水 %s -> 订单提交没有返回信息", domain.getProviderSerialNumber())
                        , "请求没有返回信息"
                        , sendResult);
        }
        logger.warn(String.format("订单提交 >> 请求返回信息: 供货流水%s -> %s", domain.getProviderSerialNumber(), sendResult));
        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = JSONObject.parseObject(sendResult);
        } catch (Exception e) {
            logger.debug(String.format("订单提交 >> 请求返回结果解析json失败: %s", sendResult));
        }
        if (resultJson == null || resultJson.size() == 0) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 供货流水 %s -> 订单提交没有返回信息", domain.getProviderSerialNumber())
                    , "没有返回信息"
                    , sendResult);
        }
        String code = null;
        String message = "";
        try {
            code = resultJson.getString("code");
            message = resultJson.getString("message");
        } catch (Exception e) {
            logger.debug(String.format("获取返回信息失败: 供货流水%s -> %s", domain.getProviderSerialNumber(), sendResult));
        }
        if (code == null) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 供货流水 %s -> 没有获取到返回状态码", domain.getProviderSerialNumber())
                    , "订单提交没有返回状态码"
                    , sendResult);
        }
        // 状态码: 下单失败
        String[] array = {"600", "602", "603", "606", "622", "623", "624", "615", "637", "751"};
        //状态码判断
        if ("2".equals(code)) { //成功
            JSONObject data = resultJson.getJSONObject("data");
            logger.debug(String.format("订单提交 >> 订单 %s -> 订单提交成功|%s %s", domain.getProviderSerialNumber(), code, message));
            result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.BUSINESS_RECHARGE_RETURN_STATUS_SUCCESS);
            result.setModel(ResultDO.SECOND_MODEL_KEY, data.getString("up_order_no"));
            return result;
        } else if(Arrays.asList(array).contains(code)){
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 供货流水 %s -> 订单提交失败|%s %s", domain.getProviderSerialNumber(), code, message)
                    , String.format("%s %s", message, code)
                    , sendResult);
        } else if("625".equals(code)){//重复订单号
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 供货流水 %s -> 订单提交失败|%s %s", domain.getProviderSerialNumber(), code, message)
                    , String.format("%s %s", message, code)
                    , sendResult);
        } else {
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 供货流水 %s -> 订单提交未正确定义状态码|%s %s", domain.getProviderSerialNumber(), code, message)
                    , String.format("%s %s", message, code)
                    , sendResult);
        }
    }

    @Override
    public ResultDO backOrder(ProviderApiDO domain, String paramStr, String bodyStr) {
        ResultDO result = new ResultSupport();
        Map<String, String> paramMap = null;
        try {
            paramMap = JSONObject.parseObject(paramStr, Map.class);
            logger.info("订单异步回调 >> " + domain.getProviderNo() + "解析后数据: " + paramStr);
        } catch (Exception e) {
            logger.info("订单异步回调 >> " + domain.getProviderNo() + "解析json失败:" + paramStr);
        }
        if (paramMap == null) {
            logger.info("订单异步回调 >> 收到异步回调,但是没有回调信息");
            result.setSuccess(false);
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
            return result;
        }
        Map<String, String> beforeParamMap = null;
        //签名
        String afterSign = "";
        try {
            //加密前参数
            beforeParamMap = paramMap.entrySet().stream().filter(item -> !"sign".equals(item.getKey()) && !"channel_type".equals(item.getKey()) && !"desc".equals(item.getKey()) && item.getValue() != null).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            Map<String, String> signParamMap = Maps.newLinkedHashMap();
            beforeParamMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(e -> signParamMap.put(e.getKey(), String.valueOf(e.getValue())));
            logger.info("加密前的数据: " + signParamMap);
            String beforeSign = (MapTool.join(signParamMap, "")).replace("=", "") + domain.getPrivateKey();
            logger.info("before_sign: " + beforeSign);
            afterSign = MD5Util.md5(beforeSign).toLowerCase();
            logger.info("afterSign: " + afterSign);
        } catch (Exception e) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("签名异常", domain.getProviderId())
                    , "签名异常"
                    , null);
        }
        if(afterSign.equals(String.valueOf(paramMap.get("sign")))){
            //订单状态
            String recharge_status = String.valueOf(paramMap.get("recharge_status"));
            //商户订单号
            String client_order_no = String.valueOf(paramMap.get("client_order_no"));
            //平台订单号
            String up_order_no = String.valueOf(paramMap.get("up_order_no"));
            //凭证
            String elecardID = String.valueOf(paramMap.get("elecardID"));
            if("2".equals(recharge_status)){//成功
                logger.info(String.format("订单异步回调 >> 订单异步返回:%s 成功 status: %s", client_order_no, recharge_status));
                result = providerApiService.updateCallback(client_order_no, elecardID, up_order_no, Constant.CALL_BACK_SUCCESS);
            } else if("6".equals(recharge_status)){//失败
                logger.info(String.format("订单异步回调 >> 订单异步返回:%s 失败 status: %s", client_order_no, recharge_status));
                result = providerApiService.updateCallback(client_order_no, "", up_order_no, Constant.CALL_BACK_FAIL);
            } else {//未确认
                logger.info(String.format("订单异步回调 >> 订单异步返回:%s 未确认 status: %s", client_order_no, recharge_status));
                result = providerApiService.updateCallback(client_order_no, "", up_order_no, Constant.CALL_BACK_NUKNOW);
            }
            if (!result.isSuccess()) {
                logger.info("订单异步回调 >> 更新订单(" + client_order_no + ")状态失败");
                result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
                return result;
            }
            result.setModel(ResultDO.FIRST_MODEL_KEY, "SUCCESS");
        } else {
            logger.info("订单异步回调 >> 签名校验: 加密前 >>" + beforeParamMap);
            logger.info(String.format("订单异步回调 >> 订单:%s 签名校验不一致 {本地签名:%s 返回签名:%s }", String.valueOf(paramMap.get("client_order_no")), afterSign, String.valueOf(paramMap.get("sign"))));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
        }
        return result;
    }

    @Override
    public ResultDO queryOrder(OrderStatusQueryDO domain) {
        ResultDO result = new ResultSupport();
        // 秒级时间戳
        String request_time = LocalDateUtils.getTodayByFormat(LocalDateUtils.DATE_FORMAT_SHORT6);
        Map<String, String> requestParamMap = new HashMap<>();
        requestParamMap.put("mrch_no", domain.getUserId());
        requestParamMap.put("request_time", request_time);
        requestParamMap.put("client_order_no", domain.getSerialNo());
        requestParamMap.put("order_time", request_time);
        String sign = "";
        try {
            Map<String, String> signParamMap = Maps.newLinkedHashMap();
            requestParamMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(e -> signParamMap.put(e.getKey(), String.valueOf(e.getValue())));
            String beforeSign = (MapTool.join(signParamMap, "")).replace("=", "") + domain.getPrivateKey();
            logger.info("before_sign: " + beforeSign);
            sign = MD5Util.md5(beforeSign);
        } catch (Exception e) {
            logger.info("查询订单 >> 签名加密失败" + e.getMessage());
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "签名加密失败");
            return result;
        }
        requestParamMap.put("sign", sign);

//        Map<String, String> sendParamMap = new HashMap<>();
//        sendParamMap.put("data", JSONObject.toJSONString(requestParamMap));
        // 发送请求
        logger.info(String.format("查询订单 >> 发送GET请求(订单号%s): url %s , param %s", domain.getSerialNo(), domain.getOrderFindUrl(), requestParamMap));
        String sendResult = HttpClientUtil.doPostJson(domain.getOrderFindUrl(), JSONObject.toJSONString(requestParamMap));
        switch (sendResult) {
            case "500":
                logger.info(String.format("查询订单 >> 订单 %s -> HTTP请求失败|500", domain.getSerialNo()));
                result.setSuccess(false);
                result.setModel(ResultDO.THIRD_MODEL_KEY, "HTTP请求失败");
                return result;
            case "504":
                logger.info(String.format("查询订单 >> 订单 %s -> HTTP请求超时|504", domain.getSerialNo()));
                result.setSuccess(false);
                result.setModel(ResultDO.THIRD_MODEL_KEY, "HTTP请求超时");
                return result;
            case "":
                logger.info(String.format("查询订单 >> 订单 %s -> 请求没有返回信息", domain.getSerialNo()));
                result.setSuccess(false);
                result.setModel(ResultDO.THIRD_MODEL_KEY, "请求没有返回信息");
                return result;
        }
        logger.info(String.format("查询订单 >> 请求返回信息: 订单 %s -> %s", domain.getSerialNo(), sendResult));
        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = JSONObject.parseObject(sendResult);
        } catch (Exception e) {
            logger.info(String.format("查询订单 >> 请求返回结果解析JSON失败: %s", sendResult));
        }
        if (resultJson.size() == 0) {
            logger.info("查询订单 >> 未找到请求返回结果");
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "未找到请求返回结果");
            return result;
        }
        String code = null;
        String desc = "";
        try {
            code = resultJson.getString("code");
            desc = resultJson.getString("message");
        } catch (Exception e) {
            logger.info(String.format("查询订单 >> 获取返回信息失败: 订单 %s -> %s", domain.getSerialNo(), sendResult));
        }
        if (code == null) {
            logger.info("查询订单 >> 未获取到返回状态码");
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "未获取到返回状态码");
            return result;
        }
        // 查询状态码: 充值成功
        String[] array = {"4","8","9"};

        // 查询状态码: 订单失败
        String[] array1 = {"5","6","7"};

        // 查询状态码: 充值中
        String[] array2 = {"0","1","2","3"};

        if ("2".equals(code)) { //请求成功
            String status = resultJson.getString("recharge_status");
            String voucher = resultJson.getString("elecardID");
            String errorMessage = resultJson.getString("desc");
            if ("2".equals(status)) { //成功
                logger.info(String.format("查询订单 >> 订单 %s -> 订单成功|%s", domain.getSerialNo(), errorMessage));
                result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_SUCCESS);
                result.setModel(ResultDO.SECOND_MODEL_KEY, voucher);
            } else if ("6".equals(status)) {//失败
                logger.info(String.format("查询订单 >> 订单 %s -> 订单失败|%s", domain.getSerialNo(), errorMessage));
                result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_FAIL);
            } else if ("1".equals(status)) {//充值中
                logger.info(String.format("查询订单 >> 订单 %s -> 订单充值中|%s", domain.getSerialNo(), errorMessage));
                result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_PROCESS);
            } else {//其它状态码则为充值中
                logger.info(String.format("查询订单 >> 订单 %s -> 订单未知状态码|%s %s", domain.getSerialNo(), errorMessage, status));
                result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_PROCESS);
            }
        } else if (code.equals("626")) {//订单不存在
            logger.info(String.format("查询订单 >> 订单 %s -> 订单查询订单不存在|%s", domain.getSerialNo(), desc));
            result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NOT_EXIST);
            result.setModel(ResultDO.FOURTH_MODEL_KEY, 10);
        } else {//未知
            logger.info(String.format("查询订单 >> 订单 %s -> 订单查询失败|%s", domain.getSerialNo(), desc));
            result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NUKNOW);
        }
        result.setModel(ResultDO.THIRD_MODEL_KEY, desc);
        return result;
    }

    @Override
    public ResultDO queryBalance(ProviderApiDO domain) {
        ResultDO result = new ResultSupport();
        if (domain.getPrivateKey() == null || domain.getUserId() == null) {
            result.setSuccess(false);
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }
        // 秒级时间戳
        String timestamp = LocalDateUtils.getTodayByFormat(LocalDateUtils.DATE_FORMAT_SHORT6);
        TreeMap<String, String> requestParamMap = new TreeMap<>();
        requestParamMap.put("mrch_no", domain.getUserId());
        requestParamMap.put("request_time", timestamp);
        //签名
        String afterSign = "";
        try {
            String beforeSign = (MapTool.join(requestParamMap,"")).replace("=", "") + domain.getPrivateKey();
            logger.debug("before_sign: " + beforeSign);
            afterSign = MD5Util.md5(beforeSign).toLowerCase();
        } catch (Exception e) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("渠道商ID: %s HTTP请求参数构建失败", domain.getProviderId())
                    , "HTTP请求参数构建失败"
                    , null);
        }
        requestParamMap.put("sign", afterSign);

        logger.debug(String.format("余额查询 >> 余额查询请求参数: %s", requestParamMap));

        // 发送请求
        String sendResult = HttpClientUtil.doPostJson(domain.getBalanceFindUrl(), JSONObject.toJSONString(requestParamMap));
        logger.debug(String.format("余额查询 >> 余额查询返回信息: 供应商账号 %s -> %s", domain.getUserId(), sendResult));
        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = JSON.parseObject(sendResult);
        } catch (Exception e) {
            logger.debug(String.format("余额查询 >> 请求返回结果解析JSON失败: %s", sendResult));
        }
        if (resultJson.size() == 0) {
            result.setSuccess(false);
            logger.debug(String.format("余额查询 >> 供应商账号: %s -> 余额查询没有返回信息", domain.getUserId()));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }
        String desc = resultJson.getString("message");
        try {
            JSONObject data = resultJson.getJSONObject("data");
            if(data == null || data.isEmpty() || data.size() <= 0){
                result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            } else {
                result.setModel(ResultDO.FIRST_MODEL_KEY, data.getString("balance"));
            }
        } catch (Exception e) {
            result.setModel(ResultDO.FIRST_MODEL_KEY, desc);
        }
        return result;
    }
    /**
     * <h2>提交订单返回失败</h2>
     *
     * @param success    是否处理成功
     * @param code       订单状态
     * @param logMessage 失败信息
     * @param message    供货摘要
     * @param entity     返回信息字符串
     * @return FIRST_MODEL_KEY: 订单状态
     * SECOND_MODEL_KEY: 供货摘要
     * THIRD_MODEL_KEY: 上游返回信息字符串
     */
    private ResultDO returnFail(boolean success, int code, String logMessage, String message, String entity) {
        ResultDO result = new ResultSupport();
        result.setSuccess(success);
        result.setModel(ResultDO.FIRST_MODEL_KEY, code);
        result.setModel(ResultDO.SECOND_MODEL_KEY, message);
        result.setModel(ResultDO.THIRD_MODEL_KEY, entity);
        logger.debug(logMessage);
        return result;
    }
}
